﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using IdeaNMR.Shared;
using IdeaNMR.Shared.Utils;

namespace IdeaNMR.Web.Core
{
    [Serializable]
    public class NMRObjectFassade<T> : BaseObjectFacade<T>
        where T : NMRObject, new()
    {
        protected override T GetContent()
        {
            T result = base.GetContent();

            if (this.Content == null)
            {
                result.Script = result.Content = string.Empty;

                User u = new User();
                this.User.CopyTo(u, true);

                NumberedAuthor na = new NumberedAuthor();
                na.Id = Guid.NewGuid();
                na.Index = 0;
                na.User = u;

                na.NMRObject = result;

                result.Authors.Add(na);
            }
            else
            {
                T first = new T();

                if (!this.Content.FirstVersionReference.IsLoaded)
                    this.Content.FirstVersionReference.Load();
                this.Content.FirstVersion.CopyTo(first, true);

                if (!this.Content.FirstVersion.DerivedVersions.IsLoaded)
                    this.Content.FirstVersion.DerivedVersions.Load();

                // Get all the versions and Identify the one, that corresponds to this.Content
                T version;
                var userVersions = this.Content.FirstVersion.DerivedVersions.
                                        OfType<T>().
                                        Where(vv =>
                                        {
                                            if (!vv.Authors.IsLoaded) vv.Authors.Load();
                                            return !vv.IsPrivate || vv.Authors.Any(aa =>
                                                {
                                                    if (!aa.UserReference.IsLoaded) aa.UserReference.Load();
                                                    return aa.User.Username == this.Username;
                                                });
                                        });
                foreach (T v in userVersions)
                {
                    version = new T();
                    v.CopyTo(version, true);
                    first.DerivedVersions.Add(version);

                    if (v == this.Content)
                    {
                        result = version;
                        if (!v.Authors.IsLoaded) v.Authors.Load();
                        foreach (NumberedAuthor na in v.Authors)
                        {
                            User u = new User();
                            if (!na.UserReference.IsLoaded) na.UserReference.Load();
                            na.User.CopyTo(u, true);

                            NumberedAuthor copyNa = new NumberedAuthor();
                            copyNa.User = u;
                            copyNa.Id = Guid.NewGuid();
                            copyNa.Index = na.Index;
                            result.Authors.Add(copyNa);
                        }
                    }
                }
            }
            return result;
        }

        public NMRObjectFassade(string username)
            : base(username)
        { }
        public NMRObjectFassade(Guid targetID, string username)
            : base(username)
        {
            this.LoadById(targetID);
        }

        protected override void CreateRelationships(T obj, SaveModeOptions smo)
        {
            foreach (var aaa in obj.Authors.ToArray())
            {
                this.Inmr.DeleteObject(aaa);
            }

            obj.Authors.Clear();

            foreach (NumberedAuthor nA in this.EntityObject.Authors)
            {
                NumberedAuthor a = new NumberedAuthor();
                a.Id = Guid.NewGuid();
                a.Index = nA.Index;
                //  a.User = nA.User;
                a.User = this.Inmr.Users.Where(uu => uu.Username == nA.User.Username).FirstOrDefault();

                obj.Authors.Add(a);
            }
            // Current user must be added
            if (!obj.Authors.Any(aa => aa.User.Username == this.Username))
            {
                NumberedAuthor a = new NumberedAuthor();
                a.Id = Guid.NewGuid();
                a.Index = this.EntityObject.Authors.Count;
                a.User = this.Inmr.Users.Where(uu => uu.Username == this.Username).FirstOrDefault();

                obj.Authors.Add(a);
            }

            if (smo == SaveModeOptions.Draft)
            {
                NumberedAuthor a = new NumberedAuthor();
                a.Id = Guid.NewGuid();
                a.Index = -1;
                a.User = this.User;
                obj.Authors.Add(a);
            }
        }

        protected override void LoadById(Guid TargetID)
        {
            if (TargetID == Guid.Empty)
                throw new ArgumentException("TargetID should not be empty.");

            this.Content = (from T lr in this.Inmr.BaseObjects.OfType<T>()
                            where lr.Id == TargetID &&
                                  (lr.Authors.Any(aa => aa.User.Username == this.Username) || lr.IsPrivate == false)
                            select lr).FirstOrDefault();

            if (this.Content == null)
            {
                string err = string.Format("There is not {0} for given Id: {{{1}}} and username '{2}'.", typeof(T).ToString(), TargetID, this.Username);
                throw new ApplicationException(err);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <returns>Allways true.</returns>
        /// <remarks>Essentially a current user must always be added as an author when saving.</remarks>
        public override bool isUserAnAuthor(string userName)
        {
            if (string.IsNullOrWhiteSpace(userName))
                throw new ArgumentException("userName must be non empty string.");

            //if (!this.Content.Authors.IsLoaded) this.Content.Authors.Load();

            //return this.Content.Authors.Any(aa => aa.User.Username == userName);
            return true;
        }
    }
}
